﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using InnovTech.Persistence;
using NHibernate;
using NHibernate.Bytecode;
using NHibernate.Cfg;

namespace InnovTech.Persistence
{
    public class NHDatabaseAccess<TDatabaseInfo> : IDatabaseAccess<TDatabaseInfo>
        where TDatabaseInfo : INHDatabaseInfo
    {

        #region Fields
        protected ISessionFactoryBuilder<TDatabaseInfo> _sessionFactoryBuilder;
        protected ISessionFactory _sessionFactory;
        protected TDatabaseInfo _dbInfo;
        #endregion

        #region ctor

        public NHDatabaseAccess(ISessionFactoryBuilder<TDatabaseInfo> builder)
        {
            _sessionFactoryBuilder = builder;
            _dbInfo = builder.DatabaseInfo;
        }

        #endregion

        #region Properties
        protected ISessionFactory SessionFactory
        {
            get
            {
                if (_sessionFactory == null)
                {
                    _sessionFactory = _sessionFactoryBuilder.BuildSessionFactory();
                }
                return _sessionFactory;
            }
        }

        

        #region IDatabaseAccess

        public TDatabaseInfo CurrentDatabaseInfo
        {
            get { return _dbInfo; }
        }

        public ConnectionState ConnectionState
        {
            get;
            protected set;
        }

        public Exception ConnectionException
        {
            get;
            protected set;
        }
        #endregion IDatabaseAccess

        #endregion

        #region Methods

        #region IDaoProvider Members
        public IDao CreateDao()
        {
            return new NHDao(SessionFactory.OpenSession());
        }

        public IDao<TEntity, TId> CreateDao<TEntity, TId>() where TEntity : IEntity<TId>
        {
            return new NHibernateDao<TEntity, TId>(SessionFactory.OpenSession());
        }
        #endregion

        #region Nhibernate




        public TResult DoSessionUnitWork<TResult>(Func<IDao, TResult> action)
        {
            NHSessionUnitWorker _sessionUnitWorker = new NHSessionUnitWorker(SessionFactory);
            return _sessionUnitWorker.DoUnitOfWork<TResult>(action);
        }

        public TResult DoTransactionUnitWork<TResult>(ISession session, Func<IDao, TResult> action)
        {
            NHTransactionUnitWorker _transactionUnitWorker = new NHTransactionUnitWorker(session);
            return _transactionUnitWorker.DoUnitOfWork<TResult>(action);
        }

        public void DoSessionUnitWork(Action<IDao> action)
        {
            NHSessionUnitWorker _sessionUnitWorker = new NHSessionUnitWorker(SessionFactory);
            _sessionUnitWorker.DoUnitOfWork<object>(dao =>
            {
                action(dao);
                return null;
            });
        }

        public void DoTransactionUnitWork(ISession session, Action<IDao> action)
        {
            NHTransactionUnitWorker _transactionUnitWorker = new NHTransactionUnitWorker(session);
            _transactionUnitWorker.DoUnitOfWork<object>(dao =>
            {
                action(dao);
                return null;
            });
        }

        public ISession CreateSession(IInterceptor interceptor = null)
        {
            if (interceptor == null)
                return SessionFactory.OpenSession();
            else
                return SessionFactory.OpenSession(interceptor);
        }

        #endregion

        #region IDatabaseAccess
        public ConnectionState Connect()
        {
            try
            {
                var factory = SessionFactory;
                ConnectionState = Persistence.ConnectionState.Open;
                return ConnectionState;
            }
            catch (Exception ex)
            {
                ConnectionException = ex;
                ConnectionState = Persistence.ConnectionState.Broken;
                return ConnectionState;
            }
        }


        public void Disconnect()
        {
            try
            {
                _sessionFactory.Close();
                _sessionFactory.Dispose();
                ConnectionState = Persistence.ConnectionState.Closed;
            }
            catch (Exception ex)
            {
                ConnectionException = ex;
                ConnectionState = Persistence.ConnectionState.Broken;
            }
        }
        #endregion

        #endregion Methods

        
    }
}
